package com.hww.common.util;

import android.content.ContentResolver;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Build;
import android.os.StatFs;
import android.text.TextUtils;

import com.hww.common.MyAppThis;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;

public final class FileUtils {
    /**
     * 获取系统默认的换行符的方法
     * <p>
     * 在 Windows 系统上，换行符是 \r\n。
     * 在 Unix/Linux 系统上，换行符是 \n。
     * 在 macOS 系统上，换行符是 \n。
     */
    private static final String LINE_SEP = System.getProperty("line.separator");

    private FileUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 根据文件路径获取文件对象。
     * <p>
     * 此方法用于将一个文件路径字符串转换为对应的File对象。如果路径字符串为空或仅包含空格，
     * 则方法返回null，否则返回一个新的File对象，其路径为输入的字符串。
     *
     * @param filePath 文件路径字符串。可以是相对路径或绝对路径。
     * @return 如果filePath不为空且不只包含空格，则返回对应的File对象；否则返回null。
     */
    public static File getFileByPath(final String filePath) {
        return StringUtils.isSpace(filePath) ? null : new File(filePath);
    }

    /**
     * 判断文件是否存在
     *
     * @param file 文件对象.
     * @return {@code true}: 存在 <br>{@code false}: 不存在
     */
    public static boolean isFileExists(final File file) {
        if (file == null) return false;
        if (file.exists()) {
            return true;
        }
        return isFileExists(file.getAbsolutePath());
    }

    /**
     * 判断文件是否存在
     *
     * @param filePath 文件对象的路径.
     * @return {@code true}: 存在<br>{@code false}: 不存在
     */
    public static boolean isFileExists(final String filePath) {
        File file = getFileByPath(filePath);
        if (file == null) return false;
        if (file.exists()) {
            return true;
        }
        return isFileExistsApi29(filePath);
    }

    /**
     * 检查Android Q (API级别29) 及以上版本中文件是否存在的方法。
     * 由于Android Q对文件访问进行了限制，因此需要使用ContentResolver来检查文件是否存在。
     *
     * @param filePath 文件的路径，通常是URI形式。
     * @return 如果文件存在则返回true，否则返回false。
     */
    private static boolean isFileExistsApi29(String filePath) {
        if (Build.VERSION.SDK_INT >= 29) {
            try {
                Uri uri = Uri.parse(filePath);
                ContentResolver cr = MyAppThis.getThis().getContentResolver();
                AssetFileDescriptor afd = cr.openAssetFileDescriptor(uri, "r");
                if (afd == null) return false;
                try {
                    afd.close();
                } catch (IOException ignore) {
                }
            } catch (FileNotFoundException e) {
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * 修改文件名。
     *
     * @param filePath 要重命名的文件对象的路径。
     * @param newName  新的文件名。
     * @return 如果重命名成功，则返回true；否则返回false。
     */
    public static boolean rename(final String filePath, final String newName) {
        return rename(getFileByPath(filePath), newName);
    }


    /**
     * 修改文件名。
     * <p>
     * 尝试将给定文件重命名为新名称。此方法首先检查文件是否存在且不为空，然后检查新名称是否有效，
     * 如果新名称与当前名称相同，则不需要重命名，直接返回true。如果新文件名已存在，则重命名失败。
     * 在所有检查通过后，尝试执行实际的重命名操作。
     *
     * @param file    要重命名的文件对象。
     * @param newName 新的文件名。
     * @return 如果重命名成功，则返回true；否则返回false。
     */
    public static boolean rename(final File file, final String newName) {
        // file is null then return false
        if (file == null) return false;
        // file doesn't exist then return false
        if (!file.exists()) return false;
        // the new name is space then return false
        if (StringUtils.isSpace(newName)) return false;
        // the new name equals old name then return true
        if (newName.equals(file.getName())) return true;
        File newFile = new File(file.getParent() + File.separator + newName);
        /*检查新文件名是否已存在，如果存在，则重命名失败，不存在则尝试执行重命名操作，并返回结果*/
        return !newFile.exists() && file.renameTo(newFile);
    }

    /**
     * 判断指定的File对象是否为一个存在的目录。
     *
     * @param dirPath 指定的文件夹路径。
     * @return 如果file不为空，且存在，且是一个目录，则返回true；否则返回false。
     */
    public static boolean isDir(final String dirPath) {
        return isDir(getFileByPath(dirPath));
    }

    /**
     * 判断指定的File对象是否为一个存在的目录。
     *
     * @param file 指定的File对象。
     * @return 如果file不为空，且存在，且是一个目录，则返回true；否则返回false。
     */
    public static boolean isDir(final File file) {
        return file != null && file.exists() && file.isDirectory();
    }

    /**
     * 判断指定文件路径的文件是否存在。
     *
     * @param filePath 要检查的File对象的路径。
     * @return 如果指定的File对象是一个存在的文件，则返回true；否则返回false。
     */
    public static boolean isFile(final String filePath) {
        return isFile(getFileByPath(filePath));
    }

    /**
     * 判断指定的File对象是否为一个存在的文件。
     * 该方法首先检查File对象是否不为空，然后确认该文件实际存在，并且它确实是一个文件而不是目录。
     *
     * @param file 要检查的File对象。
     * @return 如果指定的File对象是一个存在的文件，则返回true；否则返回false。
     */
    public static boolean isFile(final File file) {
        return file != null && file.exists() && file.isFile();
    }

    /**
     * 创建或检查目录存在。
     *
     * @param dirPath 文件夹路径.
     * @return {@code true}: 目录存在或创建目录成功<br>{@code false}: 其他为失败
     */
    public static boolean createOrExistsDir(final String dirPath) {
        return createOrExistsDir(getFileByPath(dirPath));
    }

    /**
     * 创建或检查目录存在。
     * <p>
     * 该方法用于判断指定的文件路径是否为一个存在的目录，如果存在则直接返回true，如果不存在则尝试创建该目录，
     * 并返回创建结果。这种方法确保了在目录不存在时尝试创建，避免了多次创建相同目录的冗余操作。
     * </p>
     *
     * @param file 指定的文件对象，代表待检查或创建的目录。
     * @return 如果文件对象不为空，且指定路径存在且为目录，或者成功创建了新目录，则返回true；否则返回false。
     */
    public static boolean createOrExistsDir(final File file) {
        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
    }

    /**
     * 创建或检查指定路径下的文件是否存在。
     *
     * @param filePath 指定的文件对象的路径。
     * @return 如果文件存在或成功创建返回true，否则返回false。
     */
    public static boolean createOrExistsFile(final String filePath) {
        return createOrExistsFile(getFileByPath(filePath));
    }


    /**
     * 创建或检查文件是否存在。
     * 如果文件不存在，则尝试创建它。如果文件存在，确保它是一个文件而不是目录。
     *
     * @param file 指定的文件对象。
     * @return 如果文件存在或成功创建返回true，否则返回false。
     */
    public static boolean createOrExistsFile(final File file) {
        if (file == null) return false;
        if (file.exists()) return file.isFile();
        if (!createOrExistsDir(file.getParentFile())) return false;
        try {
            return file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建一个新的文件，如果文件已存在，则尝试删除旧文件后再创建。
     *
     * @param filePath 指定要创建的文件对象路径。
     * @return 如果文件成功创建或已存在且成功删除后重新创建，则返回true；否则返回false。
     */
    public static boolean createFileByDeleteOldFile(final String filePath) {
        return createFileByDeleteOldFile(getFileByPath(filePath));
    }

    /**
     * 创建一个新的文件，如果文件已存在，则尝试删除旧文件后再创建。
     *
     * @param file 指定要创建的文件对象。
     * @return 如果文件成功创建或已存在且成功删除后重新创建，则返回true；否则返回false。
     */
    public static boolean createFileByDeleteOldFile(final File file) {
        if (file == null) return false;
        // 如果文件已存在且无法删除，则返回false
        if (file.exists() && !file.delete()) return false;
        if (!createOrExistsDir(file.getParentFile())) return false;
        try {
            return file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 复制文件或文件夹及其内容
     *
     * @param srcPath  源文件或文件夹对象.
     * @param destPath 目标文件或文件夹对象.
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean copy(final String srcPath,
                               final String destPath) {
        return copy(getFileByPath(srcPath), getFileByPath(destPath), null);
    }

    /**
     * 拷贝文件或文件夹及其内容
     *
     * @param srcPath  源文件或文件夹对象的路径.
     * @param destPath 目标文件或文件夹对象的路径.
     * @param listener 当遇到同名文件时的处理监听器。
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean copy(final String srcPath,
                               final String destPath,
                               final OnReplaceListener listener) {
        return copy(getFileByPath(srcPath), getFileByPath(destPath), listener);
    }

    /**
     * 复制文件或文件夹及其内容
     *
     * @param src  源文件或文件夹对象.
     * @param dest 目标文件或文件夹对象.
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean copy(final File src,
                               final File dest) {
        return copy(src, dest, null);
    }

    /**
     * 复制文件或文件夹及其内容
     *
     * @param src      源文件或文件夹对象.
     * @param dest     目标文件或文件夹对象.
     * @param listener 当遇到同名文件时的处理监听器。
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean copy(final File src,
                               final File dest,
                               final OnReplaceListener listener) {
        if (src == null) return false;
        if (src.isDirectory()) {
            return copyDir(src, dest, listener);
        }
        return copyFile(src, dest, listener);
    }

    /**
     * 拷贝文件夹及其内容。
     *
     * @param srcDir   源文件夹路径。
     * @param destDir  目标文件夹路径。
     * @param listener 当遇到同名文件时的处理监听器。
     * @return 若操作成功返回true，否则返回false。
     */
    private static boolean copyDir(final File srcDir,
                                   final File destDir,
                                   final OnReplaceListener listener) {
        return copyOrMoveDir(srcDir, destDir, listener, false);
    }

    /**
     * 复制文件。
     *
     * @param srcFile  源文件对象。
     * @param destFile 目标文件对象。
     * @param listener 替换监听器，用于处理目标文件已存在的情况。
     * @return 如果操作成功，返回true；否则返回false。
     */
    private static boolean copyFile(final File srcFile,
                                    final File destFile,
                                    final OnReplaceListener listener) {
        return copyOrMoveFile(srcFile, destFile, listener, false);
    }

    /**
     * 移动文件或文件夹及其内容
     *
     * @param srcPath  源文件或文件夹对象的路径.
     * @param destPath 目标文件或文件夹对象的路径.
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean move(final String srcPath,
                               final String destPath) {
        return move(getFileByPath(srcPath), getFileByPath(destPath), null);
    }

    /**
     * 移动文件或文件夹及其内容
     *
     * @param srcPath  源文件或文件夹对象的路径.
     * @param destPath 目标文件或文件夹对象的路径.
     * @param listener 当遇到同名文件时的处理监听器。
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean move(final String srcPath,
                               final String destPath,
                               final OnReplaceListener listener) {
        return move(getFileByPath(srcPath), getFileByPath(destPath), listener);
    }

    /**
     * 移动文件或文件夹及其内容
     *
     * @param src  源文件或文件夹对象.
     * @param dest 目标文件或文件夹对象.
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean move(final File src,
                               final File dest) {
        return move(src, dest, null);
    }

    /**
     * 移动文件或文件夹及其内容
     *
     * @param src      源文件或文件夹对象.
     * @param dest     目标文件或文件夹对象.
     * @param listener 当遇到同名文件时的处理监听器。
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean move(final File src,
                               final File dest,
                               final OnReplaceListener listener) {
        if (src == null) return false;
        if (src.isDirectory()) {
            return moveDir(src, dest, listener);
        }
        return moveFile(src, dest, listener);
    }

    /**
     * 移动文件夹及其内容。
     *
     * @param srcDir   源文件夹路径。
     * @param destDir  目标文件夹路径。
     * @param listener 当遇到同名文件时的处理监听器。
     * @return 若操作成功返回true，否则返回false。
     */
    public static boolean moveDir(final File srcDir,
                                  final File destDir,
                                  final OnReplaceListener listener) {
        return copyOrMoveDir(srcDir, destDir, listener, true);
    }

    /**
     * 移动文件。
     *
     * @param srcFile  源文件对象。
     * @param destFile 目标文件对象。
     * @param listener 替换监听器，用于处理目标文件已存在的情况。
     * @return 如果操作成功，返回true；否则返回false。
     */
    public static boolean moveFile(final File srcFile,
                                   final File destFile,
                                   final OnReplaceListener listener) {
        return copyOrMoveFile(srcFile, destFile, listener, true);
    }

    /**
     * 拷贝或移动文件夹及其内容。
     *
     * @param srcDir   源文件夹路径。
     * @param destDir  目标文件夹路径。
     * @param listener 当遇到同名文件时的处理监听器。
     * @param isMove   是否移动文件夹，默认为拷贝。
     * @return 若操作成功返回true，否则返回false。
     */
    private static boolean copyOrMoveDir(final File srcDir,
                                         final File destDir,
                                         final OnReplaceListener listener,
                                         final boolean isMove) {
        if (srcDir == null || destDir == null) return false;
        /*File.separator 获取文件路径分隔符的常量,windows是 "\" linux是 "/" */
        String srcPath = srcDir.getPath() + File.separator;
        String destPath = destDir.getPath() + File.separator;
        /*防止目标文件夹路径包含在源文件夹路径中，导致无限循环*/
        if (destPath.contains(srcPath)) return false;
        if (!srcDir.exists() || !srcDir.isDirectory()) return false;
        if (!createOrExistsDir(destDir)) return false;
        File[] files = srcDir.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                File oneDestFile = new File(destPath + file.getName());
                if (file.isFile()) {
                    if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false;
                } else if (file.isDirectory()) {
                    if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false;
                }
            }
        }
        return !isMove || deleteDir(srcDir);
    }

    /**
     * 复制或移动文件。
     *
     * @param srcFile  源文件对象。
     * @param destFile 目标文件对象。
     * @param listener 替换监听器，用于处理目标文件已存在的情况。
     * @param isMove   是否移动文件，true表示移动，false表示复制。
     * @return 如果操作成功，返回true；否则返回false。
     */
    private static boolean copyOrMoveFile(final File srcFile,
                                          final File destFile,
                                          final OnReplaceListener listener,
                                          final boolean isMove) {
        if (srcFile == null || destFile == null) return false;
        // srcFile equals destFile then return false
        if (srcFile.equals(destFile)) return false;
        // srcFile doesn't exist or isn't a file then return false
        if (!srcFile.exists() || !srcFile.isFile()) return false;
        // 如果目标文件已存在
        if (destFile.exists()) {
            // 如果监听器不为空，则调用监听器的onReplace方法，根据返回值决定是否删除目标文件
            if (listener == null || listener.onReplace(srcFile, destFile)) {// require delete the old file
                // 如果目标文件删除成功，继续执行复制或移动操作
                if (!destFile.delete()) {// unsuccessfully delete then return false
                    return false;
                }
            } else {
                // 如果监听器返回false，表示取消操作，直接返回true
                return true;
            }
        }
        // 如果目标文件的父目录不存在，则创建父目录，创建失败则操作失败
        if (!createOrExistsDir(destFile.getParentFile())) return false;
        try {
            // 从源文件读取数据并写入目标文件，如果isMove为true，则在写入成功后删除源文件
            return FileIOUtils.writeFileFromIS(destFile.getAbsolutePath(), new FileInputStream(srcFile))
                    && !(isMove && !deleteFile(srcFile));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除给定的文件或目录。
     *
     * @param filePath 要删除的文件路径。
     * @return 如果删除成功，则返回true；否则返回false。
     */
    public static boolean delete(final String filePath) {
        return delete(getFileByPath(filePath));
    }

    /**
     * 删除给定的文件或目录。
     * <p>
     * 如果参数文件不存在，此方法将简单地返回true，视为删除成功。
     * 对于目录，只有当目录为空时，才能直接删除；如果目录不为空，则需要递归删除目录下的所有文件和子目录。
     * 对于文件，直接尝试删除即可。
     * </p>
     *
     * @param file 要删除的文件或目录。
     * @return 如果删除成功，则返回true；否则返回false。
     */
    public static boolean delete(final File file) {
        if (file == null) return false;
        if (file.isDirectory()) {
            return deleteDir(file);
        }
        return deleteFile(file);
    }

    /**
     * 删除给定的目录及其内容。
     * <p>
     * 该方法会递归地删除指定目录下的所有文件和子目录。首先检查目录是否存在且为目录，
     * 然后遍历目录下的所有文件和子目录，分别删除它们。最后尝试删除空目录本身。
     * 如果任何一步失败，即有文件或子目录无法删除，整个方法将返回false。
     * </p>
     *
     * @param dir 要删除的目录。
     * @return 如果目录成功被删除，则返回true；否则返回false。
     */
    private static boolean deleteDir(final File dir) {
        if (dir == null) return false;
        // dir doesn't exist then return true
        if (!dir.exists()) return true;
        // dir isn't a directory then return false
        if (!dir.isDirectory()) return false;
        File[] files = dir.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isFile()) {
                    if (!file.delete()) return false;
                } else if (file.isDirectory()) {
                    if (!deleteDir(file)) return false;
                }
            }
        }
        return dir.delete();
    }

    /**
     * 删除文件
     *
     * @param file 要删除的文件对象.
     * @return {@code true}: 成功<br>{@code false}: 失败
     */
    private static boolean deleteFile(final File file) {
        return file != null && (!file.exists() || file.isFile() && file.delete());
    }

    /**
     * 删除指定目录下的所有文件和文件夹。
     *
     * @param dirPath 指定的目录文件对象，表示要删除其下内容的目录。
     * @return {@code true}: success<br>{@code false}: fail
     */
    public static boolean deleteAllInDir(final String dirPath) {
        return deleteAllInDir(getFileByPath(dirPath));
    }

    /**
     * 删除指定目录下的所有文件和文件夹。
     * 该方法通过传入一个目录文件对象，调用内部的带过滤器的删除方法，来实现删除该目录下所有内容的功能。
     * 注意，该方法不会删除指定的目录本身，只删除其下的文件和文件夹。
     *
     * @param dir 指定的目录文件对象，表示要删除其下内容的目录。
     * @return 如果成功删除了指定目录下的所有文件和文件夹，则返回true；否则返回false。
     */
    public static boolean deleteAllInDir(final File dir) {
        return deleteFilesInDirWithFilter(dir, new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return true;
            }
        });
    }

    /**
     * 删除指定目录下的所有文件。
     *
     * @param dirPath 需要被删除文件的目录。如果此参数为null，方法将不执行任何操作。
     * @return {@code true}: success<br>{@code false}: fail
     */
    public static boolean deleteFilesInDir(final String dirPath) {
        return deleteFilesInDir(getFileByPath(dirPath));
    }

    /**
     * 删除指定目录下的所有文件。
     * 该方法通过传入的文件夹路径，递归删除该文件夹下所有的文件。对于目录中的子目录，不会进行递归删除，
     * 只会删除直接位于该目录下的文件。如果目录不存在或者不是一个目录，则不会进行任何操作。
     *
     * @param dir 需要被删除文件的目录。如果此参数为null，方法将不执行任何操作。
     * @return 如果删除成功返回true，否则返回false。如果dir参数为null或者传入的路径不存在，
     * 方法会返回false。
     */
    public static boolean deleteFilesInDir(final File dir) {
        return deleteFilesInDirWithFilter(dir, new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isFile();
            }
        });
    }

    /**
     * 删除目录中满足过滤条件的所有文件。
     * <p>
     * 此方法用于递归地删除指定目录中满足特定过滤条件的所有文件和子目录。
     * 如果目录不存在或者为空，则认为删除操作成功。
     *
     * @param dirPath 要删除文件的目录。如果为null，则方法直接返回false。
     * @param filter  文件过滤器，用于选择要删除的文件。如果为null，则方法直接返回false。
     * @return 如果成功删除了所有选定的文件和目录，则返回true；否则返回false。
     */
    public static boolean deleteFilesInDirWithFilter(final String dirPath,
                                                     final FileFilter filter) {
        return deleteFilesInDirWithFilter(getFileByPath(dirPath), filter);
    }

    /**
     * 删除目录中满足过滤条件的所有文件。
     * <p>
     * 此方法用于递归地删除指定目录中满足特定过滤条件的所有文件和子目录。
     * 如果目录不存在或者为空，则认为删除操作成功。
     *
     * @param dir    要删除文件的目录。如果为null，则方法直接返回false。
     * @param filter 文件过滤器，用于选择要删除的文件。如果为null，则方法直接返回false。
     * @return 如果成功删除了所有选定的文件和目录，则返回true；否则返回false。
     */
    public static boolean deleteFilesInDirWithFilter(final File dir, final FileFilter filter) {
        if (dir == null || filter == null) return false;
        // dir doesn't exist then return true
        if (!dir.exists()) return true;
        // dir isn't a directory then return false
        if (!dir.isDirectory()) return false;
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (filter.accept(file)) {
                    if (file.isFile()) {
                        if (!file.delete()) return false;
                    } else if (file.isDirectory()) {
                        if (!deleteDir(file)) return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表(不支持递归查找与自定义排序)。
     *
     * @param dirPath 指定的目录。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final String dirPath) {
        return listFilesInDir(dirPath, null);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表(不支持递归查找与自定义排序)。
     *
     * @param dir 指定的目录。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final File dir) {
        return listFilesInDir(dir, null);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表，支持自定义排序(不进行递归查找)。
     *
     * @param dirPath    指定的目录。
     * @param comparator 用于排序返回的文件列表的比较器。可以为null，表示不需要排序。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final String dirPath, Comparator<File> comparator) {
        return listFilesInDir(getFileByPath(dirPath), false, comparator);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表，支持自定义排序(不进行递归查找)。
     *
     * @param dir        指定的目录。
     * @param comparator 用于排序返回的文件列表的比较器。可以为null，表示不需要排序。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final File dir, Comparator<File> comparator) {
        return listFilesInDir(dir, false, comparator);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表，支持递归查找。
     *
     * @param dirPath     指定的目录。
     * @param isRecursive 是否递归查找文件。如果为true，则会查找指定目录及其子目录下的所有文件；如果为false，则只查找指定目录下的文件。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final String dirPath, final boolean isRecursive) {
        return listFilesInDir(getFileByPath(dirPath), isRecursive);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表，支持递归查找。
     *
     * @param dir         指定的目录。
     * @param isRecursive 是否递归查找文件。如果为true，则会查找指定目录及其子目录下的所有文件；如果为false，则只查找指定目录下的文件。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final File dir, final boolean isRecursive) {
        return listFilesInDir(dir, isRecursive, null);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表，支持递归查找和自定义排序。
     *
     * @param dirPath     指定的目录。
     * @param isRecursive 是否递归查找文件。如果为true，则会查找指定目录及其子目录下的所有文件；如果为false，则只查找指定目录下的文件。
     * @param comparator  用于排序返回的文件列表的比较器。可以为null，表示不需要排序。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final String dirPath,
                                            final boolean isRecursive,
                                            final Comparator<File> comparator) {
        return listFilesInDir(getFileByPath(dirPath), isRecursive, comparator);
    }

    /**
     * 此方法提供了一个灵活的方式來获取文件列表，支持递归查找和自定义排序。
     *
     * @param dir         指定的目录。
     * @param isRecursive 是否递归查找文件。如果为true，则会查找指定目录及其子目录下的所有文件；如果为false，则只查找指定目录下的文件。
     * @param comparator  用于排序返回的文件列表的比较器。可以为null，表示不需要排序。
     * @return 返回一个包含指定目录下所有文件的列表。列表中的文件顺序可能根据提供的比较器进行排序，或者保持原始顺序。
     */
    public static List<File> listFilesInDir(final File dir,
                                            final boolean isRecursive,
                                            final Comparator<File> comparator) {
        return listFilesInDirWithFilter(dir, new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return true;
            }
        }, isRecursive, comparator);
    }

    /**
     * 获取目录下满足过滤条件的文件列表(不递归子目录，不排序)。
     *
     * @param dirPath 指定的目录路径。
     * @param filter  文件过滤器，用于筛选文件。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final String dirPath,
                                                      final FileFilter filter) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter);
    }

    /**
     * 获取目录下满足过滤条件的文件列表(不递归子目录，不排序)。
     *
     * @param dir    指定的目录。
     * @param filter 文件过滤器，用于筛选文件。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final File dir,
                                                      final FileFilter filter) {
        return listFilesInDirWithFilter(dir, filter, false, null);
    }

    /**
     * 获取目录下满足过滤条件的文件列表(不递归子目录)。
     *
     * @param dirPath    指定的目录路径。
     * @param filter     文件过滤器，用于筛选文件。
     * @param comparator 文件排序比较器，用于定义文件的排序方式。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final String dirPath,
                                                      final FileFilter filter,
                                                      final Comparator<File> comparator) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, comparator);
    }

    /**
     * 获取目录下满足过滤条件的文件列表(不递归子目录)。
     *
     * @param dir        指定的目录。
     * @param filter     文件过滤器，用于筛选文件。
     * @param comparator 文件排序比较器，用于定义文件的排序方式。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final File dir,
                                                      final FileFilter filter,
                                                      final Comparator<File> comparator) {
        return listFilesInDirWithFilter(dir, filter, false, comparator);
    }

    /**
     * 获取目录下满足过滤条件的文件列表。
     * 该方法允许通过指定过滤器、是否递归搜索以及文件的排序方式来定制文件搜索行为。
     *
     * @param dirPath     指定的目录路径。
     * @param filter      文件过滤器，用于筛选文件。
     * @param isRecursive 是否递归搜索子目录。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final String dirPath,
                                                      final FileFilter filter,
                                                      final boolean isRecursive) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive);
    }

    /**
     * 获取目录下满足过滤条件的文件列表。
     * 该方法允许通过指定过滤器、是否递归搜索以及文件的排序方式来定制文件搜索行为。
     *
     * @param dir         指定的目录。
     * @param filter      文件过滤器，用于筛选文件。
     * @param isRecursive 是否递归搜索子目录。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final File dir,
                                                      final FileFilter filter,
                                                      final boolean isRecursive) {
        return listFilesInDirWithFilter(dir, filter, isRecursive, null);
    }


    /**
     * 获取目录下满足过滤条件的文件列表。
     * 该方法允许通过指定过滤器、是否递归搜索以及文件的排序方式来定制文件搜索行为。
     *
     * @param dirPath     指定的目录路径。
     * @param filter      文件过滤器，用于筛选文件。
     * @param isRecursive 是否递归搜索子目录。
     * @param comparator  文件排序比较器，用于定义文件的排序方式。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final String dirPath,
                                                      final FileFilter filter,
                                                      final boolean isRecursive,
                                                      final Comparator<File> comparator) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive, comparator);
    }

    /**
     * 获取目录下满足过滤条件的文件列表。
     * 该方法允许通过指定过滤器、是否递归搜索以及文件的排序方式来定制文件搜索行为。
     *
     * @param dir         指定的目录。
     * @param filter      文件过滤器，用于筛选文件。
     * @param isRecursive 是否递归搜索子目录。
     * @param comparator  文件排序比较器，用于定义文件的排序方式。
     * @return 返回满足过滤条件的文件列表。
     */
    public static List<File> listFilesInDirWithFilter(final File dir,
                                                      final FileFilter filter,
                                                      final boolean isRecursive,
                                                      final Comparator<File> comparator) {
        List<File> files = listFilesInDirWithFilterInner(dir, filter, isRecursive);
        if (comparator != null) {
            Collections.sort(files, comparator);
        }
        return files;
    }

    /**
     * 列出目录中符合过滤条件的文件列表。
     * 该方法支持递归列出子目录中的文件。
     *
     * @param dir         需要列出文件的目录。
     * @param filter      文件过滤器，用于筛选文件。
     * @param isRecursive 是否递归列出子目录中的文件。
     * @return 符合过滤条件的文件列表。
     */
    private static List<File> listFilesInDirWithFilterInner(final File dir,
                                                            final FileFilter filter,
                                                            final boolean isRecursive) {
        List<File> list = new ArrayList<>();
        if (!isDir(dir)) return list;
        File[] files = dir.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (filter.accept(file)) {
                    list.add(file);
                }
                if (isRecursive && file.isDirectory()) {
                    list.addAll(listFilesInDirWithFilterInner(file, filter, true));
                }
            }
        }
        return list;
    }

    /**
     * 获取文件的最后修改时间
     *
     * @param filePath 指定的文件对象路径.
     * @return 如果文件存在且参数非null，则返回文件的最后修改时间，以毫秒为单位；否则返回-1。
     */

    public static long getFileLastModified(final String filePath) {
        return getFileLastModified(getFileByPath(filePath));
    }

    /**
     * 获取文件的最后修改时间。
     * <p>
     * 此方法用于获取指定文件的最后修改时间。如果文件不存在或参数为null，则返回-1。
     *
     * @param file 指定的文件对象。
     * @return 如果文件存在且参数非null，则返回文件的最后修改时间，以毫秒为单位；否则返回-1。
     */
    public static long getFileLastModified(final File file) {
        if (file == null) return -1;
        return file.lastModified();
    }

    /**
     * 简单判断文件的字符集。
     * 通过读取文件的前两个字节来判断文件是否为UTF-8、Unicode或GBK编码。
     *
     * @param filePath 待检测的文件路径。
     * @return 文件的字符集，可能是"UTF-8"、"Unicode"、"UTF-16BE"或"GBK"。
     */
    public static String getFileCharsetSimple(final String filePath) {
        return getFileCharsetSimple(getFileByPath(filePath));
    }

    /**
     * 简单判断文件的字符集。
     * 通过读取文件的前两个字节来判断文件是否为UTF-8、Unicode或GBK编码。
     *
     * @param file 待检测的文件。
     * @return 文件的字符集，可能是"UTF-8"、"Unicode"、"UTF-16BE"或"GBK"。
     */
    public static String getFileCharsetSimple(final File file) {
        if (file == null) return "";
        if (isUtf8(file)) return "UTF-8";
        int p = 0;
        InputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(file));
            p = (is.read() << 8) + is.read();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        switch (p) {
            case 0xfffe:
                return "Unicode";
            case 0xfeff:
                return "UTF-16BE";
            default:
                return "GBK";
        }
    }

    /**
     * 判断文件是否使用UTF-8编码。
     *
     * @param filePath 待检查的文件。
     * @return 如果文件使用UTF-8编码，则返回true；否则返回false。
     */
    public static boolean isUtf8(final String filePath) {
        return isUtf8(getFileByPath(filePath));
    }

    /**
     * 判断文件使用的字符集是否为UTF-8编码。
     *
     * @param file 待检查的文件。
     * @return 如果文件使用UTF-8编码，则返回true；否则返回false。
     */
    public static boolean isUtf8(final File file) {
        if (file == null) return false;
        InputStream is = null;
        try {
            byte[] bytes = new byte[24];
            is = new BufferedInputStream(new FileInputStream(file));
            int read = is.read(bytes);
            if (read != -1) {
                byte[] readArr = new byte[read];
                System.arraycopy(bytes, 0, readArr, 0, read);
                return isUtf8(readArr) == 100;
            } else {
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 检查字节数组是否符合UTF-8编码规范。
     * <p>
     * UTF-8编码方式
     * ----------------------------------------------
     * 0xxxxxxx
     * 110xxxxx 10xxxxxx
     * 1110xxxx 10xxxxxx 10xxxxxx
     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
     *
     * @param raw 待检查的字节数组。
     * @return 如果字节数组完全符合ASCII编码，返回100；
     * 如果字节数组是UTF-8编码，返回编码比例的整数；
     * 如果字节数组不符合UTF-8编码，返回0。
     */
    private static int isUtf8(byte[] raw) {
        int i, len;
        int utf8 = 0, ascii = 0;
        if (raw.length > 3) {
            if ((raw[0] == (byte) 0xEF) && (raw[1] == (byte) 0xBB) && (raw[2] == (byte) 0xBF)) {
                return 100;
            }
        }
        len = raw.length;
        int child = 0;
        for (i = 0; i < len; ) {
            // 如果字节为0xFF或0xFE，不符合UTF-8规范
            if ((raw[i] & (byte) 0xFF) == (byte) 0xFF || (raw[i] & (byte) 0xFE) == (byte) 0xFE) {
                return 0;
            }
            if (child == 0) {
                // 如果字节是ASCII码  0x0*******
                if ((raw[i] & (byte) 0x7F) == raw[i] && raw[i] != 0) {
                    ascii++;
                } else if ((raw[i] & (byte) 0xC0) == (byte) 0xC0) {
                    // 0x11****** maybe is UTF-8
                    for (int bit = 0; bit < 8; bit++) {
                        if ((((byte) (0x80 >> bit)) & raw[i]) == ((byte) (0x80 >> bit))) {
                            child = bit;
                        } else {
                            break;
                        }
                    }
                    utf8++;
                }
                i++;
            } else {
                child = (raw.length - i > child) ? child : (raw.length - i);
                boolean currentNotUtf8 = false;
                for (int children = 0; children < child; children++) {
                    // format must is 0x10******
                    if ((raw[i + children] & ((byte) 0x80)) != ((byte) 0x80)) {
                        if ((raw[i + children] & (byte) 0x7F) == raw[i + children] && raw[i] != 0) {
                            // ASCII format is 0x0*******
                            ascii++;
                        }
                        currentNotUtf8 = true;
                    }
                }
                if (currentNotUtf8) {
                    utf8--;
                    i++;
                } else {
                    utf8 += child;
                    i += child;
                }
                child = 0;
            }
        }
        // UTF-8 contains ASCII
        if (ascii == len) {
            return 100;
        }
        return (int) (100 * ((float) (utf8 + ascii) / (float) len));
    }

    /**
     * 计算给定文件的行数。
     * 本方法通过读取文件内容并检测行分隔符（'\n'或'\r'）来确定文件的行数。
     * 它处理了不同操作系统可能使用的不同行结束符。
     *
     * @param filePath 指定的文件对象路径，行数将从此文件中计算。
     * @return 返回文件的行数。
     */
    public static int getFileLines(final String filePath) {
        return getFileLines(getFileByPath(filePath));
    }


    /**
     * 计算给定文件的行数。
     * 本方法通过读取文件内容并检测行分隔符（'\n'或'\r'）来确定文件的行数。
     * 它处理了不同操作系统可能使用的不同行结束符。
     *
     * @param file 指定的文件对象，行数将从此文件中计算。
     * @return 返回文件的行数。
     */
    public static int getFileLines(final File file) {
        int count = 1;
        InputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(file));
            byte[] buffer = new byte[1024];
            int readChars;
            if (LINE_SEP.endsWith("\n")) {
                while ((readChars = is.read(buffer, 0, 1024)) != -1) {
                    for (int i = 0; i < readChars; ++i) {
                        if (buffer[i] == '\n') ++count;
                    }
                }
            } else {
                while ((readChars = is.read(buffer, 0, 1024)) != -1) {
                    for (int i = 0; i < readChars; ++i) {
                        if (buffer[i] == '\r') ++count;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return count;
    }

    /**
     * 计算文件或目录的大小。
     * 通过递归处理目录，计算目录下所有文件的大小。
     * 对于文件，直接返回其大小。
     *
     * @param filePath 指定的文件或目录的路径。
     * @return 文件或目录的大小，以字符串形式表示。
     */
    public static String getSize(final String filePath) {
        return getSize(getFileByPath(filePath));
    }

    /**
     * 计算文件或目录的大小。
     * 通过递归处理目录，计算目录下所有文件的大小。
     * 对于文件，直接返回其大小。
     *
     * @param file 指定的文件或目录。
     * @return 文件或目录的大小，以字符串形式表示。
     */
    public static String getSize(final File file) {
        if (file == null) return "";
        if (file.isDirectory()) {
            return getDirSize(file);
        }
        return getFileSize(file);
    }


    /**
     * 计算给定目录的大小。
     * 该方法通过递归遍历目录下的所有文件和子目录，累加它们的大小，从而得到目录的总大小。
     * 如果目录不存在或者无法访问，则返回空字符串。
     *
     * @param dir 需要计算大小的目录
     * @return 目录的大小，以适合阅读的格式表示（如"10.5 MB"），如果无法计算则返回空字符串
     */
    private static String getDirSize(final File dir) {
        long len = getDirLength(dir);
        return len == -1 ? "" : ConvertUtils.byte2FitMemorySize(len);
    }

    /**
     * 获取文件的大小，以适合阅读的格式表示。
     * 如果文件长度无法获取，则返回空字符串。
     *
     * @param file 需要获取大小的文件对象。
     * @return 文件大小的字符串表示，如果无法获取则返回空字符串。
     */
    private static String getFileSize(final File file) {
        long len = getFileLength(file);
        return len == -1 ? "" : ConvertUtils.byte2FitMemorySize(len);
    }

    /**
     * 获取文件或目录的总长度。
     * 如果参数file是目录，则计算该目录下所有文件和子目录的长度总和；
     * 如果参数file是文件，则返回该文件的长度。
     *
     * @param filePath 指定的文件或目录的路径
     * @return 文件或目录的总长度。如果file为null，或无法访问file，则返回0。
     */
    public static long getLength(final String filePath) {
        return getLength(getFileByPath(filePath));
    }

    /**
     * 获取文件或目录的总长度。
     * 如果参数file是目录，则计算该目录下所有文件和子目录的长度总和；
     * 如果参数file是文件，则返回该文件的长度。
     *
     * @param file 指定的文件或目录
     * @return 文件或目录的总长度。如果file为null，或无法访问file，则返回0。
     */
    public static long getLength(final File file) {
        if (file == null) return 0;
        if (file.isDirectory()) {
            return getDirLength(file);
        }
        return getFileLength(file);
    }

    /**
     * 计算指定目录下所有文件的总长度。
     * 递归地遍历目录下的每个子目录和文件，累加文件的长度。
     * 目录的长度定义为目录下所有文件的长度之和。
     *
     * @param dir 待计算长度的目录。如果此参数不是目录或者不存在，则返回0。
     * @return 目录下所有文件的总长度。如果目录为空，也返回0。
     */
    private static long getDirLength(final File dir) {
        if (!isDir(dir)) return 0;
        long len = 0;
        File[] files = dir.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isDirectory()) {
                    /*如果是子目录，递归计算子目录的长度并累加*/
                    len += getDirLength(file);
                } else {
                    /*如果是文件，累加文件的长度*/
                    len += file.length();
                }
            }
        }
        return len;
    }

    /**
     * 获取文件的长度。
     * <p>
     * 此方法首先检查文件路径是否为URL格式。如果是URL，则通过网络请求获取文件长度；
     * 如果不是URL，则认为是本地文件路径，通过本地文件系统获取文件长度。
     *
     * @param filePath 文件的路径，可以是URL或本地文件路径。
     * @return 文件的长度。如果文件不存在或无法访问，则返回-1。
     */
    public static long getFileLength(final String filePath) {
        boolean isURL = filePath.matches("[a-zA-z]+://[^\\s]*");
        if (isURL) {
            try {
                HttpsURLConnection conn = (HttpsURLConnection) new URL(filePath).openConnection();
                conn.setRequestProperty("Accept-Encoding", "identity");
                conn.connect();
                if (conn.getResponseCode() == 200) {
                    return conn.getContentLength();
                }
                return -1;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return getFileLength(getFileByPath(filePath));
    }

    /**
     * 获取文件的长度。
     *
     * @param file 指定的文件对象，用于获取长度。
     * @return 如果文件存在且是一个正常的文件，则返回文件的长度；否则返回-1。
     */
    private static long getFileLength(final File file) {
        if (!isFile(file)) return -1;
        return file.length();
    }

    /**
     * 计算文件的MD5值返回十六进制字符串。
     *
     * @param filePath 要计算MD5值的文件。
     * @return 文件的MD5字节数组。如果文件为null或计算过程中出现异常，则返回null。
     */
    public static String getFileMD5ToString(final String filePath) {
        File file = StringUtils.isSpace(filePath) ? null : new File(filePath);
        return getFileMD5ToString(file);
    }

    /**
     * 计算文件的MD5值返回十六进制字符串。
     *
     * @param file 要计算MD5值的文件。
     * @return 文件的MD5字节数组。如果文件为null或计算过程中出现异常，则返回null。
     */
    public static String getFileMD5ToString(final File file) {
        return ConvertUtils.bytes2HexString(getFileMD5(file));
    }

    /**
     * 计算文件的MD5值。
     *
     * @param filePath 要计算MD5值的文件路径。
     * @return 文件的MD5字节数组。如果文件为null或计算过程中出现异常，则返回null。
     */
    public static byte[] getFileMD5(final String filePath) {
        return getFileMD5(getFileByPath(filePath));
    }

    /**
     * 计算文件的MD5值。
     *
     * @param file 要计算MD5值的文件。
     * @return 文件的MD5字节数组。如果文件为null或计算过程中出现异常，则返回null。
     */
    public static byte[] getFileMD5(final File file) {
        if (file == null) return null;
        DigestInputStream dis = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            MessageDigest md = MessageDigest.getInstance("MD5");
            dis = new DigestInputStream(fis, md);
            byte[] buffer = new byte[1024 * 256];
            while (true) {
                if (!(dis.read(buffer) > 0)) break;
            }
            md = dis.getMessageDigest();
            return md.digest();
        } catch (NoSuchAlgorithmException | IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (dis != null) {
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取文件路径中的目录部分。
     * 该方法通过解析文件的绝对路径来提取目录部分。如果文件对象为空，则直接返回空字符串。
     * 对于非空的文件对象，首先获取其绝对路径，然后调用 {@code getDirName} 方法处理这个绝对路径，
     * 以获得路径中的目录部分。
     *
     * @param file 文件对象，可以是任意文件，包括目录。
     * @return 返回文件路径中的目录部分，如果文件对象为空，则返回空字符串。
     */
    public static String getDirName(final File file) {
        if (file == null) return "";
        return getDirName(file.getAbsolutePath());
    }

    /**
     * 获取文件路径中的目录部分。
     * <p>
     * 该方法用于从给定的文件路径中提取目录部分。如果路径不存在或仅包含空格，则返回空字符串。
     * 如果路径中包含文件分隔符，则返回最后一个分隔符之前的部分，包括分隔符本身。
     *
     * @param filePath 文件路径，可以是相对路径或绝对路径。
     * @return 返回提取出的目录路径，如果不存在目录或输入为空白字符串，则返回空字符串。
     */
    public static String getDirName(final String filePath) {
        if (StringUtils.isSpace(filePath)) return "";
        int lastSep = filePath.lastIndexOf(File.separator);
        return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1);
    }

    /**
     * 获取文件名。
     *
     * @param file 文件路径字符串。
     * @return 提取的文件名或路径本身，如果它不包含目录分隔符。
     */
    public static String getFileName(final File file) {
        if (file == null) return "";
        return getFileName(file.getAbsolutePath());
    }

    /**
     * 获取文件名。
     * 从给定的文件路径中提取文件名。如果路径为空或仅包含空格，返回空字符串。
     * 如果路径中包含目录分隔符，文件名被定义为最后一个目录分隔符之后的部分。
     * 如果路径不包含目录分隔符，整个路径被视作文件名。
     *
     * @param filePath 文件路径字符串。
     * @return 提取的文件名或路径本身，如果它不包含目录分隔符。
     */
    public static String getFileName(final String filePath) {
        if (StringUtils.isSpace(filePath)) return "";
        int lastSep = filePath.lastIndexOf(File.separator);
        return lastSep == -1 ? filePath : filePath.substring(lastSep + 1);
    }

    /**
     * 获取不带扩展名的文件名。
     *
     * @param file 文件的完整路径，可能包含扩展名。
     * @return 不带扩展名的文件名。如果路径为空或仅包含空格，返回空字符串；
     * 如果路径中没有扩展名，返回整个路径；如果路径中包含路径分隔符，
     * 返回最后一个路径分隔符后的部分。
     */
    public static String getFileNameNoExtension(final File file) {
        if (file == null) return "";
        return getFileNameNoExtension(file.getPath());
    }

    /**
     * 获取不带扩展名的文件名。
     * 该方法用于从文件路径中提取不包含扩展名的文件名。如果路径中不存在扩展名，或者文件名中包含路径分隔符，
     * 方法将根据这些条件返回适当的文件名。
     *
     * @param filePath 文件的完整路径，可能包含扩展名。
     * @return 不带扩展名的文件名。如果路径为空或仅包含空格，返回空字符串；
     * 如果路径中没有扩展名，返回整个路径；如果路径中包含路径分隔符，
     * 返回最后一个路径分隔符后的部分。
     */
    public static String getFileNameNoExtension(final String filePath) {
        if (StringUtils.isSpace(filePath)) return "";
        /*查找最后一个点的位置，点通常用于分隔文件名和扩展名*/
        int lastPoi = filePath.lastIndexOf('.');
        /*查找最后一个路径分隔符的位置*/
        int lastSep = filePath.lastIndexOf(File.separator);
        if (lastSep == -1) {
            return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi));
        }
        if (lastPoi == -1 || lastSep > lastPoi) {
            return filePath.substring(lastSep + 1);
        }
        return filePath.substring(lastSep + 1, lastPoi);
    }

    /**
     * 获取文件路径中的文件扩展名。
     * 注意：此方法不验证文件路径的有效性，仅处理路径字符串。
     *
     * @param file 文件对象。
     * @return 文件扩展名。如果不存在文件扩展名或输入为空白字符串，则返回空字符串。
     */
    public static String getFileExtension(final File file) {
        if (file == null) {
            return "";
        }
        return getFileExtension(file.getPath());
    }

    /**
     * 获取文件路径中的文件扩展名。
     * 文件扩展名是文件名最后的部分，由一个点(.)分隔。如果文件名中没有点或点不是最后的部分，则不认为有文件扩展名。
     * <p>
     * 注意：此方法不验证文件路径的有效性，仅处理路径字符串。
     *
     * @param filePath 文件路径字符串。
     * @return 文件扩展名。如果不存在文件扩展名或输入为空白字符串，则返回空字符串。
     */
    public static String getFileExtension(final String filePath) {
        if (StringUtils.isSpace(filePath)) {
            return "";
        }
        int lastPoi = filePath.lastIndexOf('.');
        int lastSep = filePath.lastIndexOf(File.separator);
        if (lastPoi == -1 || lastSep >= lastPoi) {
            return "";
        }
        return filePath.substring(lastPoi + 1);
    }

    /**
     * 通知系统扫描指定的文件。
     *
     * @param filePath 需要被扫描的文件。如果文件为null或不存在，则方法不执行任何操作。
     */
    public static void notifySystemToScan(final String filePath) {
        notifySystemToScan(getFileByPath(filePath));
    }

    /**
     * 通知系统扫描指定的文件。
     * <p>
     * 此方法用于向系统发送一个广播，请求系统媒体扫描器扫描指定的文件。这通常在添加新媒体文件后使用，
     * 以便系统能立即识别并索引新文件，使其在媒体库中可用。
     *
     * @param file 需要被扫描的文件。如果文件为null或不存在，则方法不执行任何操作。
     */
    public static void notifySystemToScan(final File file) {
        if (file == null || !file.exists()) {
            return;
        }
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        intent.setData(Uri.parse("file://" + file.getAbsolutePath()));
        MyAppThis.getThis().sendBroadcast(intent);
    }

    /**
     * 获取给定路径所在文件系统的总大小(系统存储总大小)。
     * 通过StatFs类来获取文件系统的统计信息，进而计算出文件系统的总大小。
     *
     * @param anyPathInFs 文件系统中的任意路径，用于获取文件系统统计信息。
     * @return 文件系统的总大小，如果路径为空或无效，则返回0。
     */
    public static long getFsTotalSize(String anyPathInFs) {
        if (TextUtils.isEmpty(anyPathInFs)) {
            return 0;
        }
        StatFs statFs = new StatFs(anyPathInFs);
        long blockSize;
        long totalSize;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            blockSize = statFs.getBlockSizeLong();
            totalSize = statFs.getBlockCountLong();
        } else {
            blockSize = statFs.getBlockSize();
            totalSize = statFs.getBlockCount();
        }
        return blockSize * totalSize;
    }

    /**
     * 通过传入文件系统中的任意路径，来获取该文件系统当前可用的空间大小。--(获取系统剩余空间大小)
     * 使用StatFs类来获取文件系统的统计信息，并根据Android版本的不同使用相应的长整型方法来避免整型溢出。
     *
     * @param anyPathInFs 文件系统中的任意路径，用于获取文件系统统计信息。
     * @return 返回文件系统的可用大小，单位为字节。如果路径为空或无效，则返回0。
     */
    public static long getFsAvailableSize(final String anyPathInFs) {
        if (TextUtils.isEmpty(anyPathInFs)) {
            return 0;
        }
        StatFs statFs = new StatFs(anyPathInFs);
        long blockSize;
        long availableSize;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            blockSize = statFs.getBlockSizeLong();
            availableSize = statFs.getAvailableBlocksLong();
        } else {
            blockSize = statFs.getBlockSize();
            availableSize = statFs.getAvailableBlocks();
        }
        return blockSize * availableSize;
    }

    ///////////////////////////////////////////////////////////////////////////
    // interface
    ///////////////////////////////////////////////////////////////////////////

    public interface OnReplaceListener {
        /**
         * 当文件替换操作发生时被调用的方法。
         *
         * @param srcFile  源文件，即准备被替换的原文件对象。
         * @param destFile 目标文件，即用于替换源文件的新文件对象。
         * @return 返回布尔值。如果返回true，表示文件替换操作被允许执行；
         * 如果返回false，则表示操作被取消。
         */
        boolean onReplace(File srcFile, File destFile);
    }

}
